#include "common/formator.h"

#include "common/node_utils.h"

namespace optimizer {
std::string Format::ToString(const std::shared_ptr<Operator>& op) {
  Format formator;
  return formator.ToStringInternal(op);
}

std::string Format::ToStringInternal(const std::shared_ptr<Operator>& op) {
  ss_.str("");
  return FormatOperatorTree(op);
}

std::string Format::FormatOperatorTree(const std::shared_ptr<Operator>& op, bool is_last) {
  for (const auto& prefix : prefix_)
    ss_ << prefix;

  if (first_)
    first_ = false;
  else {
    if (is_last) {
      ss_ << "└── ";
      prefix_.emplace_back("    ");
    } else {
      ss_ << "├── ";
      prefix_.emplace_back("│   ");
    }
  }

  ss_ << op->ToString() << '\n';

  auto print_exprs = [&](const std::shared_ptr<Operator>& op) {
    if (op->GetProject() == nullptr && op->GetFilter() == nullptr)
      return;
    if (op->GetProject() != nullptr) {
      for (size_t i = 0; i < prefix_.size() - 1; i++)
        ss_ << prefix_[i];
      ss_ << "├── " << "output: " << NodeUtils::ToString(op->GetProject()) << std::endl;
    }
    if (op->GetFilter() != nullptr) {
      for (size_t i = 0; i < prefix_.size() - 1; i++)
        ss_ << prefix_[i];
      ss_ << "├── " << "filter: " << NodeUtils::ToString(op->GetFilter()) << std::endl;
    }
  };

  prefix_.emplace_back("│   ");
  print_exprs(op);
  prefix_.pop_back();

  const auto& children = op->GetChildren();
  for (size_t i = 0; i < children.size(); i++)
    FormatOperatorTree(children[i], i == children.size() - 1);

  if (!prefix_.empty())
    prefix_.pop_back();

  return ss_.str();
}

}  // namespace optimizer